/**
 * SPDX-FileCopyrightText: 2023-2025 Sangfor Technologies Inc.
 * SPDX-License-Identifier: Mulan PSL v2
 */
<template>
    <div class="sfv-datefield">
        <div v-if="disabled" class="sfv-datefield-disabled-mask"></div>
        <div ref="dateInput"
             :class="getTextfieldCls"
             :style="getTextfieldStyle">
            <input v-model="dateDisplay"
                   v-qtip="invalidText"
                   class="sfv-textfield_input"
                   :style="getTextfieldStyle"
                   :title="dateDisplay"
                   :class="invalidClass"
                   :placeholder="placeholder"
                   :disabled="disabled"
                   :readonly="true"
                   :qcls="invalidType"
                   :qanchor="invalidPosition" />

            <div ref="btns" class="sfv-textfield-btns">
                <button class="sfv-textfield_trigger" type="button">
                    <i class="sf-comp-iconfont icon-comp-calendar"></i>
                </button>
            </div>
        </div>

        <sf-layer ref="dateLayer"
                  class="sfv-datefield_layer"
                  default-target="dateInput"
                  offset="0 4"
                  :auto-scrollbar="false"
                  @hide="_hideLayer">
            <div v-if="isDefault" class="sfv-datefield_picker">
                <sf-datepicker ref="datePicker"
                               v-model="dateValue"
                               class="sfv-datefield_picker"
                               :start-limit="startLimit"
                               :end-limit="endLimit"
                               :selection-mode="selectionMode"
                               @select="_confirmDate">
                </sf-datepicker>
                <div v-if="showClear" class="sfv-datefield_picker-btns">
                    <sf-button class="sfv-btn sfv-btn-small" @click="clearValue(true)">
                        <lang>清空</lang>
                    </sf-button>
                </div>
            </div>

            <div v-if="isDateTime" class="sfv-datefield_picker">
                <sf-datepicker ref="datePicker"
                               v-model="dateValue"
                               class="sfv-datefield_picker"
                               :start-limit="startLimit"
                               :end-limit="endLimit">
                </sf-datepicker>
                <div class="sfv-datefield_picker-time">
                    <span class="sfv-datefield_picker-time-title"><lang>选择时间：</lang></span>
                    <sf-timefield v-model="timeValue" :default-width="timeFieldWidth" :showSecond="showSecond"></sf-timefield>
                </div>
                <div class="sfv-datefield_picker-time-btn">
                    <sf-button v-if="showClear"
                               size="small"
                               class="pull-left m-l5"
                               @click="clearValue(true)">
                        <lang>{{ clearText }}</lang>
                    </sf-button>
                    <sf-button-primary
                        size="small"
                        :default-disabled="!dateValue"
                        @click="_confirmDate">
                        <lang>确定</lang>
                    </sf-button-primary>
                    <sf-button size="small"
                               @click="_cancelDateTimePick">
                        <lang>取消</lang>
                    </sf-button>
                </div>
            </div>

            <div v-if="isRange" class="sfv-datefield_picker">
                <sf-daterangepicker ref="datePicker"
                                    v-model="dateValue"
                                    class="sfv-datefield_picker"
                                    :start-limit="startLimit"
                                    :end-limit="endLimit"
                                    :prev-left="prevLeft"
                                    @select="_selectRangeDate">
                </sf-daterangepicker>
                <div class="sfv-datefield_picker-btns">
                    <sf-button-primary
                               size="small"
                               :default-disabled="!isRangeSelected"
                               @click="_confirmDate">
                        <lang>确定</lang>
                    </sf-button-primary>
                    <sf-button v-if="showClear" size="small" @click="clearValue(true)">
                        <lang>清空</lang>
                    </sf-button>
                </div>
            </div>

            <div v-if="isDateTimeRange" class="sfv-datefield_picker">
                <sf-daterangepicker ref="datePicker"
                                    v-model="dateValue"
                                    class="sfv-datefield_picker"
                                    :start-limit="startLimit"
                                    :prev-left="prevLeft"
                                    :end-limit="endLimit"
                                    @select="_selectRangeDate">
                </sf-daterangepicker>
                <div class="sfv-datefield_picker-times clearfix">
                    <div class="sfv-datefield_picker-time-box">
                        <sf-textfield :value="selectStartDate"
                                      :default-readonly="true"
                                      :default-disabled="!isRangeTimeEnable"
                                      :default-width="dayFieldWidth"/>
                        <sf-timefield v-model="startTimeValue"
                                      :default-width="timeFieldWidth"
                                      :default-disabled="!isRangeTimeEnable"
                                      :show-second="showSecond"></sf-timefield>
                    </div>
                    <div class="sfv-datefield_picker-time-box sfv-datefield_picker-time-box--right">
                        <i class="sf-comp-iconfont icon-comp-arrow-right-big sfv-datefield_picker-icon-arrow"></i>
                        <sf-textfield :value="selectEndDate"
                                      :default-readonly="true"
                                      :default-disabled="!isRangeTimeEnable"
                                      :default-width="dayFieldWidth"/>
                        <sf-timefield v-model="endTimeValue"
                                      :default-width="timeFieldWidth"
                                      :default-disabled="!isRangeTimeEnable"
                                      :show-second="showSecond"></sf-timefield>
                    </div>
                </div>
                <div class="sfv-datefield_picker-btns">
                    <sf-button-primary
                               size="small"
                               :default-disabled="!isRangeSelected || isTimeOver"
                               @click="_confirmDate">
                        <lang>确定</lang>
                    </sf-button-primary>
                    <sf-button v-if="showClear" size="small" @click="clearValue(true)">
                        <lang>清空</lang>
                    </sf-button>
                    <sf-button v-else size="small" @click="_cancelDateTimePick">
                        <lang>取消</lang>
                    </sf-button>
                </div>
            </div>
        </sf-layer>
    </div>

</template>

<script>

/**
 * sf-datefield
 * */

import {encodeDate, decodeDate} from '@sxf/sf-vue-component/src/util/format';
import {
    isDate,
    isObject,
    isFunction,
    isString,
    isNumber,
    typeOf
} from '@sxf/util/type_of';
import Component from '@sxf/sf-vue-component/src/widgets/component';
import JsonField from '@sxf/sf-vue-component/src/widgets/form/json_field';
import Util from '@sxf/sf-vue-component/src/widgets/datepicker/util';

const FORMAT_MONTH = 'Y-m';
const FORMAT_YMD = 'Y-m-d';
const FORMAT_HI = 'Y-m-d H:i';
const FORMAT_FULL = 'Y-m-d H:i:s';

/**
 * 从值中获取 Date 对象
 *      提取自之前的 _setValue
 * @param {Date | String | Number} value
 * @returns {Date}
 */
let getDate = (value) => isString(value) ? decodeDate(value) : new Date(value);

/**
 * 从值中获取时分秒信息
 * @param {Date | String | Number} value
 * @returns {{hour: number, minute: number, second: number}}
 */
let getTimeValue = (value) => {
    let dateTime = getDate(value);
    return {
        hour: dateTime.getHours(),
        minute: dateTime.getMinutes(),
        second: dateTime.getSeconds()
    };
};

/**
 * 是否是正常的时分秒, 因为我们 watch timefield 的值时, 异常的值也会出来
 * @param {Object} time
 * @returns {boolean}
 */
let isTimeNormal = (time) => {
    const MAX_HOUR = 24;
    const MAX_MINUTE_OR_SECOND = 60;

    return Number(time.hour) < MAX_HOUR &&
        Number(time.minute) < MAX_MINUTE_OR_SECOND &&
        Number(time.second) < MAX_MINUTE_OR_SECOND;
};

let defaultTimeFactory = (time = {}) => ({hour: 0,
    minute: 0,
    second: 0, ...time});

export default {
    mixins: [
        Component,
        JsonField
    ],

    props: {

        // 限制起始日期, 格式必须为 YYYY-MM-DD, 用于条件判断和直接传给 datepicker 等
        startLimit: String,

        // 限制结束日期, 与 startLimit 一样
        endLimit: String,

        // 类型: single || month || monthRange || week || range || dateTime || dateTimeRange
        type: {
            type: String,
            default: 'single'
        },

        // 当配置 type = dateTime 时，该配置生效，用于配置是否显示秒
        showSecond: {
            type: Boolean,
            default: false
        },

        // 自定义 format 函数, 用于输入框显示格式化, 形如 Y-m-d H:i:s
        format: {
            type: [String, Function],
            default: 'Y-m-d'
        },

        // 输入项是否允许为空
        allowBlank: {
            type: Boolean,
            'default': true
        },

        // 输入项为空的时候的提示文字
        blankText: {
            type: String,
            'default': () => _('该输入项不允许为空')
        },

        // 占位的内容
        placeholder: {
            type: String,
            default: () => {
                return _('请选择日期');
            }
        },

        // 是否显示清空的按钮
        showClear: {
            type: Boolean,
            default: false
        },

        // value
        value: {},

        /**
         * 输出的数据类型，可选值：string || number || date
         * string输出格式化后的日期格式，number为毫秒级时间戳，date为日期对象
         */
        valueFormat: {
            type: String
        },
           // 清空文本
        clearText: {
            type: String,
            'default': () => _('清空')
        },

        // 日期时间选择器中的时间的默认值，格式为 {hour: 0,minute: 0,second: 0}
        defaultTime: {
            type: Object,
            default: null
        },

        // 范围日期时间选择器中开始时间的默认值，格式为 {hour: 0,minute: 0,second: 0}
        defaultStartTime: {
            type: Object,
            default: null
        },

        // 范围日期时间选择器中结束时间的默认值，格式为 {hour: 0,minute: 0,second: 0}
        defaultEndTime: {
            type: Object,
            default: null
        },

        // 范围日期时，当v-model为空时，配置当月在右，上月在左，默认本月在左，下月在右
        prevLeft: {
            type: Boolean,
            default: false
        },
        beforeSubmit: {
            type: Function,
            default: () => (()=> true)
        },

        dayFieldWidth: {
            type: Number,
            default: 110
        },

        // 选择时间时，时间输入项宽度
        timeFieldWidth: {
            type: Number,
            default: 130
        },

        // 尺寸, '' || 'small'
        size: {
            type: String,
            default: ''
        }
    },

    data () {
        let isMonth = this.type === 'month';
        let isMonthRange = this.type === 'monthRange';
        let isWeek = this.type === 'week';
        let isRange = this.type === 'range';
        let isDateTime = this.type === 'dateTime';
        let isDateTimeRange = this.type === 'dateTimeRange';
        let valueType = isRange || isDateTimeRange || (isWeek && isObject(this.value)) ?
            typeOf(this.value.start) :
            typeOf(this.value);

        return {
            dateValue: this.value,
            valueType: this.valueFormat || valueType,
            timeValue: defaultTimeFactory(this.defaultTime),
            startTimeValue: defaultTimeFactory(this.defaultStartTime),
            endTimeValue: defaultTimeFactory(this.defaultEndTime),
            dateDisplay: '',
            isDefault: !isRange && !isDateTime && !isDateTimeRange,
            isWeek: isWeek,
            isMonth: isMonth,
            isMonthRange: isMonthRange,
            isDateTime: isDateTime,
            isRange: isRange,
            isRangeSelected: this.value && this.value.start && this.value.end,
            isDateTimeRange,
            isTimeOver: false,  // type 为 dateTimeRange 时, start 的时间是否比 end 晚, 如时间选同一天, 但是选 8:00 到 1:00
            formatFn: (() => {
                if (isMonth || isMonthRange) {
                    return FORMAT_MONTH;
                }

                if ((isDateTimeRange || isDateTime) && this.format === FORMAT_YMD) {
                    if (this.showSecond) {
                        return FORMAT_FULL;
                    }

                    return FORMAT_HI;
                }

                return this.format;
            })()
        };
    },

    computed: {
        selectionMode () {
            if (['single', 'range', 'dateTime'].includes(this.type)) {
                return 'date';
            }
            return this.type;
        },

        isRangeTimeEnable () {
            return this.dateValue && this.dateValue.start && this.dateValue.end;
        },

        selectStartDate () {
            return this.dateValue.start ? encodeDate(this.dateValue.start, 'Y-m-d') : '';
        },

        selectEndDate () {
            return this.dateValue.end ? encodeDate(this.dateValue.end, 'Y-m-d') : this.selectStartDate;
        },

        getTextfieldCls () {
            return [
                'sfv-textfield',
                this.size ? 'sfv-textfield--' + this.size : ''
            ];
        },

        getTextfieldStyle () {
            return {
                width: typeof this.width === 'number' ? this.width + 'px' : this.width
            };
        }
    },

    mounted () {
        this._setValue(this.value);
        this._formatDateDisplay(this.dateValue);
        this.$watch('startTimeValue', this._handleStartOrEndTimeChange, {deep: true});
        this.$watch('endTimeValue', this._handleStartOrEndTimeChange, {deep: true});
    },

    watch: {
        value (value) {
            this._formatDateDisplay(value);
            this._setValue(value);
        },
        valueFormat (value) {
            this.valueType = value;
        },
        isRangeSelected (value, oldValue) {
            if (oldValue && !value) {
                if (this.defaultStartTime) {
                    this.startTimeValue = defaultTimeFactory(this.defaultStartTime);
                } else {
                    this.startTimeValue = getTimeValue('');
                }
                if (this.defaultEndTime) {
                    this.endTimeValue = defaultTimeFactory(this.defaultEndTime);
                } else {
                    this.endTimeValue = getTimeValue('');
                }
            }
        }
    },

    methods: {

        _cancelDateTimePick () {
            let dateLayer = this.$refs.dateLayer;
            if (dateLayer) {
                dateLayer.hidden = true;
                this._setValue(this.value); // 把原来的值重新设置回去
            }
        },

        _closest (el, selector) {
            let hasSelector = false;
            let matchesSelector =
                    el.matches ||
                    el.webkitMatchesSelector ||
                    el.mozMatchesSelector ||
                    el.msMatchesSelector;

            while (el) {
                if (matchesSelector.call(el, selector)) {
                    hasSelector = true;
                    break;
                }

                el = el.parentElement;
            }

            return hasSelector;
        },

        _hideLayer () {
            this.$emit('hide');
            this._setValue(this.value);
        },

        _formatDateDisplay (value) {
            let formatStr = isFunction(this.formatFn) ?
                this.formatFn() :
                this.formatFn;

            if (this.type === 'week') {
                return this._formatWeekDisplay(value, formatStr);
            }

            if (this.type === 'month') {
                return this._formatMonthOrYearDisplay(value, formatStr);
            }

            if (this.type === 'year') {
                return this._formatMonthOrYearDisplay(value, 'Y');
            }

            if (!value || this._isRangeNull(value)) {
                this.dateDisplay = '';
                return ;
                
            }

            if (isDate(value)) {
                this.dateDisplay = encodeDate(value, formatStr);
            } else if (isObject(value)) {
                this.dateDisplay = _('{0} ~ {1}', encodeDate(value.start, formatStr), encodeDate(value.end, formatStr));
            } else if (isNumber(value)) {
                this.dateDisplay = encodeDate(new Date(value), formatStr);
            } else {
                this.dateDisplay = encodeDate(decodeDate(value), formatStr);
            }
        },

        _formatMonthOrYearDisplay (value, format) {
            if (isDate(value) || isNumber(value)) {
                this.dateDisplay = encodeDate(new Date(value), format);
            } else if (isObject(value)) {
                this.dateDisplay = encodeDate(value.start || value.end, format);
            } else {
                this.dateDisplay = value ? encodeDate(decodeDate(value), format) : '';
            }
        },

        _formatWeekDisplay (value, format) {

            // 判断是否该显示空
            let showDisplayEmpty =
                    !value ||
                    this._isRangeNull(value) ||
                    Util.isOverLimit(this.startLimit, this.endLimit, value);
            if (showDisplayEmpty) {
                this.dateDisplay = '';
                return;
            }

            let {start, end} = Util.getWeekRangeByDate(value);
            start = encodeDate(start, format);
            end = encodeDate(end, format);
            this.dateDisplay = _('{0} ~ {1}', start, end);
        },

        _isRangeNull (value) {
            return isObject(value) && !value.start && !value.end;
        },

        /**
         * 根据时间重新设置日历面板上的选中日期或时间
         * @param {Date|Object|String|Number} value
         * */
        _setValue (value) {
            this.dateValue = value;

            if (this.type === 'week') {
                this._setWeekValue(value);
            }

            if (this.isDateTime) {
                if (this.defaultTime && !value) {
                    this.timeValue = defaultTimeFactory(this.defaultTime);
                } else {
                    this.timeValue = getTimeValue(value);
                }
            }

            if (this.isDateTimeRange) {
                let start = value && value.start;
                let end = value && value.end;

                if (this.defaultStartTime && !start) {
                    this.startTimeValue = defaultTimeFactory(this.defaultStartTime);
                } else {
                    this.startTimeValue = getTimeValue(start || '');
                }

                if (this.defaultEndTime && !end) {
                    this.endTimeValue = defaultTimeFactory(this.defaultEndTime);
                } else {
                    this.endTimeValue = getTimeValue(end || '');
                }
            }
        },

        _setWeekValue (value) {
            let setEmptyRange =
                    !value ||
                    this._isRangeNull(value) ||
                    Util.isOverLimit(this.startLimit, this.endLimit, value);
            if (setEmptyRange) {
                this.dateValue = {start: '', end: ''};
                return;
            }
            let {start, end} = Util.getWeekRangeByDate(value);
            this.dateValue = {start, end};
        },

        _parseDateValue (value) {
            if (isObject(value)) {
                return {
                    start: this._parseDateValue(value.start),
                    end: this._parseDateValue(value.end)
                };
            }
            switch (this.valueType.toLowerCase()) {
                case 'string':
                    return encodeDate(value, 'Y-m-d');
                case 'number':
                    return isDate(value) ? value.getTime() : value;
                default:
                    return value;
            }
        },

        _parseDateTimeValue (dateValue, timeValue) {
            let dateTime = this._buildDateTime(dateValue, timeValue);

            switch (this.valueType.toLowerCase()) {
                case 'string':
                    return encodeDate(dateTime, 'Y-m-d H:i:s');
                case 'number':
                    return isDate(dateTime) ? dateTime.getTime() : dateTime;
                default:
                    return dateTime;
            }
        },

        _selectRangeDate (start, end) {
            this.isRangeSelected = start && end;

            /**
             * range 选择了某一项
             * @param {String} start
             * @param {String} end
             */
            this.$emit('select', start, end);
        },

        _buildDateTime (date, time) {
            let dateTime = getDate(date);
            if (time) {
                dateTime.setHours(time.hour);
                dateTime.setMinutes(time.minute);
                dateTime.setSeconds(time.second);
            }
            return dateTime;
        },

        _beforeSubmit (date, time) {
            if (isObject(date)) {
                date = {
                    start: this._buildDateTime(date.start, time.start),
                    end: this._buildDateTime(date.end, time.end)
                };
            } else {
                date = this._buildDateTime(date, time);
            }
            return this.beforeSubmit(date);
        },

        _confirmDate () {
            let $picker = this.$refs.datePicker;
            let $layer = this.$refs.dateLayer;
            if (!$picker) {
                return;
            }
            let dateValue = $picker.getDateValue();
            if (this.selectionMode === 'week') {
                if (isObject(this.value)) {
                    dateValue = Util.getWeekRangeByDate(dateValue);
                } else {
                    dateValue = dateValue.start;
                }
            }
            let retValue = this._parseDateValue(dateValue);
            let timeValue = this.timeValue;

            if (this.isDateTime) {
                retValue = this._parseDateTimeValue(dateValue, this.timeValue);
            }

            if (this.isDateTimeRange) {
                retValue.start = this._parseDateTimeValue(dateValue.start, this.startTimeValue);
                retValue.end = this._parseDateTimeValue(dateValue.end, this.endTimeValue);
                timeValue = {
                    start: this.startTimeValue,
                    end: this.endTimeValue
                };
            }

            if (this._beforeSubmit(dateValue, timeValue) === false) {
                return;
            }


            /**
             * input
             * @param {Any} retValue
             */
            this.$emit('input', retValue);
            this.$emit('confirm', retValue);

            if (['month', 'year', 'week'].includes(this.selectionMode)) {

                /**
                 * 月, 年, 周选择的事件
                 * @param {Object} {start: "", end: ""}
                 */
                this.$emit('range', this._getRange(dateValue));
            }

            if ($layer) {
                $layer.hide();
            }
        },

        /**
         *  选择模式为年、月、周时对外暴露的用于获取对应选择范围日期的接口
         * @param {Date|String|Number} date
         * @returns {Array}
         */
        _getRange (date) {
            date = Util.newDate(date.start);
            let year = date.getFullYear();
            let month = date.getMonth();

            if (this.selectionMode === 'week') {
                return this._parseDateValue(Util.getWeekRangeByDate(date));
            }

            if (this.selectionMode === 'year') {
                return this._parseDateValue(Util.getRangeOfYear(year));
            }

            if (this.selectionMode === 'month') {
                return this._parseDateValue(Util.getRangeOfMonth(year, month));
            }
        },

        isValueEmpty () {
            let value = this.getJsonValue();
            return typeof value === 'undefined' || value === null || value === '';
        },

        /**
         * 校验是否允许为空
         * @param {String} value    下拉框的值
         * @param {Array}  errors   原有错误列表，校验不过则把错误添加到列表里
         * @private
         */
        _validateBlank (value, errors) {
            if (typeof value === 'undefined' || value === null || value === '') {
                if (this.allowBlank) {
                    return;
                }
                errors.push(this.blankText);
            }
        },

        /**
         * 传入了validator函数，自定义验证
         * @param {String} value    下拉框的值
         * @param {Array}  errors   原有错误列表，校验不过则把错误添加到列表里
         * @private
         */
        _validateValidator (value, errors) {
            if (typeof this.validator === 'function') {
                let msg = this.validator(value);
                if (msg !== true) {
                    errors.push(msg);
                }
            }
        },

        /**
         * 处理开始时间和结束时间的变化, 看是不是有选了同一天但是 startTime 晚于 endTime 的情况
         * @private
         */
        _handleStartOrEndTimeChange () {
            let $picker = this.$refs.datePicker;
            if (!this.isDateTimeRange || !this.isRangeSelected || !$picker) {
                return;
            }
            if (!isTimeNormal(this.startTimeValue) || !isTimeNormal(this.endTimeValue)) {
                return;
            }

            let dateValue = $picker.getDateValue();

            let start = this._parseDateTimeValue(dateValue.start, this.startTimeValue);
            let end = this._parseDateTimeValue(dateValue.end, this.endTimeValue);
            this.isTimeOver = Util.isDateOver(start, end);
        },

        /**
         * 校验
         * @param {String}  value   下拉框的值
         * @returns {Array}         错误列表
         */
        getErrors (value) {
            let errors = [];
            value = typeof value === 'undefined' ? this.getJsonValue() : value;

            this._validateBlank(value, errors);
            this._validateValidator(value, errors);

            return errors;
        },

        /**
         * 清空值
         * @param {Boolean} isHideLayer 是否隐藏 layer
         */
        clearValue (isHideLayer) { // 复制原有 _clearRangeDate 逻辑
            let retValue = '';
            let getEmptyObj = () => ({
                start: '',
                end: ''
            });

            if (this.isRange || this.isDateTimeRange) {
                retValue = getEmptyObj();
                this.isRangeSelected = false;
            }
            if (this.isWeek) {
                retValue = getEmptyObj();
            }

            this.$emit('input', retValue);

            /**
             * 清空值
             */
            this.$emit('clear');

            if (isHideLayer) {
                let $layer = this.$refs.dateLayer;
                if ($layer) {
                    $layer.hide();
                }
            }
        }
    }
};
</script>
